package com.youshang.nio;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.IntStream;

/**
 * 学习 Selector
 * channel 注册 selector
 * @author youshang
 * @date 2021/09/06 23:57
 **/
public class Test8  {

    public static void main(String[] args) throws Exception{
        //nio 三个组建， selector  channel  buffer
        // 1. 调用 open 静态方法，使用系统默认的java.nio.channels.spi.SelectorProvider 创建一个新的 selector
        // 2. 还可以通过调用 java.nio.channels.spi.SelectorProvider#openSelector 自定义selector
        // 当不调用close，会一直处于打开状态

        //可选的channel和selector的注册是由 SelectionKey 对象组成。
        // SelectionKey由三种结构维护着
        // 1. key set 包括所有现在channel 注册 selector的表现，可以通过 keys()  查看。
        // 2. selected-key set，selector 选中的 selectionKeys，是 key set 的子集。
        // 3. cancelled-key 已经取消的selectionKeys，但channel并未取消注册，只是无法访问而已（由下一次覆盖）, 是 key set 的子集

        int[] ports = new int[5];
        IntStream.range(0,5).forEach(i -> {
            ports[i] = 6000+i;
        });

        Selector selector = Selector.open();
        for (int i = 0; i < ports.length; i++) {
            int port = ports[i];
            //创建ServerSocketChannel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置为非堵塞的
            serverSocketChannel.configureBlocking(false);
            ServerSocket socket = serverSocketChannel.socket();
            InetSocketAddress address = new InetSocketAddress(port);
            socket.bind(address);

            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("监听端口 = " + port);
        }
        while (true){
            int select = selector.select();
            System.out.println("select = " + select);
            //获取到当前选定的所有selectionKeys
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("当前所有选定的selectionKeys: = " + selectionKeys);

            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    //通过Selectionkey 获取 SelectableChannel 通道
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
                    //当前通道接收socket连接
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //设置为非堵塞的
                    socketChannel.configureBlocking(false);

                    //将当前 channel 注册到 selector,并且设置当前SelectionKey 为只读
                    socketChannel.register(selector, SelectionKey.OP_READ);

                    //System.out.println("当前channel "+socketChannel+" 注册到 selector : "+selector+" 返回的selectionKey = " + register);
                    //当通道使用完后一定要移除
                    iterator.remove();

                }else if (selectionKey.isReadable()){
                    //根据当前的selectionKey获取到 channel
                    SocketChannel socketChannel = (SocketChannel)selectionKey.channel();

                    int bytesRead = 0;
                    while (true){
                        //定制一个byteBuffer
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                        //将limit归位(limit = capacity，position = 0)
                        byteBuffer.clear();
                        //从channel中获取数据
                        int read = socketChannel.read(byteBuffer);
                        if (read <= 0){
                            break;
                        }
                        //读写转换
                        byteBuffer.flip();
                        socketChannel.write(byteBuffer);
                        bytesRead += read;

                        System.out.println("获取读到的内容 = " + new String(byteBuffer.array()));
                        //System.out.println(byteBuffer);
                    }
                    System.out.println("bytesRead : = "+ bytesRead + "   "+ socketChannel);
                    //当通道使用完后一定要移除
                    iterator.remove();
                }else if (selectionKey.isWritable()){
                    System.out.println(selectionKey);
                }
            }


        }
    }
}
